package gitlog;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.diff.DiffEntry;
import org.eclipse.jgit.diff.RenameDetector;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.errors.StopWalkException;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.revwalk.filter.RevFilter;
import org.eclipse.jgit.treewalk.TreeWalk;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.ParseException;
import java.util.*;
import java.util.List;

public class GitLog {
    public static final String CONFIG_PROPERTIES = "config.properties";
    public static final String GIT_DIR_KEY = "gitDir";
    public static final String AUTHOR_KEY = "author";
    public static final String START_TIME_KEY = "startTime";
    public static final String END_TIME_KEY = "endTime";

    private JTextField dirTextField;
    private JButton selDirButton;
    private JPanel jPanel;
    private JTextArea fileListTextArea;
    private JTextField authorTextField;
    private JTextField startTimeTextField;
    private JTextField endTimeTextField;
    private JButton findButton;
    private JProgressBar progressBar1;

    private Properties properties = new Properties();

    public static void main(String[] args) {
        JFrame frame = new JFrame("GitLog");
        GitLog gitLog = new GitLog();
        frame.setContentPane(gitLog.jPanel);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                super.windowClosing(e);
                try {
                    gitLog.exit();
                } catch (Exception e1) {
                    e1.printStackTrace();
                }
            }
        });
        frame.setSize(800, 600);
        int windowWidth = frame.getWidth(); //获得窗口宽
        int windowHeight = frame.getHeight(); //获得窗口高
        Toolkit kit = Toolkit.getDefaultToolkit(); //定义工具包
        Dimension screenSize = kit.getScreenSize(); //获取屏幕的尺寸
        int screenWidth = screenSize.width; //获取屏幕的宽
        int screenHeight = screenSize.height; //获取屏幕的高
        frame.setLocation(screenWidth / 2 - windowWidth / 2, screenHeight / 2 - windowHeight / 2);//设置窗口居中显示
        frame.setVisible(true);
    }

    public GitLog() {
        FileInputStream inStream = null;
        try {
            File file = new File(CONFIG_PROPERTIES);
            if (!file.exists()) {
                file.createNewFile();
            } else {
                inStream = new FileInputStream(file);
                properties.load(inStream);
                String gitDir = properties.getProperty(GIT_DIR_KEY);
                String author = properties.getProperty(AUTHOR_KEY);
                String startTime = properties.getProperty(START_TIME_KEY);
                String endTime = properties.getProperty(END_TIME_KEY);
                dirTextField.setText(gitDir);
                authorTextField.setText(author);
                startTimeTextField.setText(startTime);
                endTimeTextField.setText(endTime);
            }
            System.out.println(file.getAbsolutePath());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(inStream != null) {
                try {
                    inStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        startTimeTextField.addFocusListener(new FocusAdapter() {
            @Override
            public void focusLost(FocusEvent e) {
                String startTimeStr = startTimeTextField.getText();
                if (StringUtils.isNotBlank(startTimeStr)) {
                    try {
                        DateUtils.parseDate(startTimeStr, "yyyy-MM-dd hh:mm:ss");
                    } catch (ParseException p) {
                        JOptionPane.showMessageDialog(jPanel, "起始时间格式为yyyy-MM-dd hh:mm:ss");
                    }
                }
            }
        });

        endTimeTextField.addFocusListener(new FocusAdapter() {
            @Override
            public void focusLost(FocusEvent e) {
                String endTimeStr = endTimeTextField.getText();
                if (StringUtils.isNotBlank(endTimeStr)) {
                    try {
                        DateUtils.parseDate(endTimeStr, "yyyy-MM-dd hh:mm:ss");
                    } catch (ParseException p) {
                        JOptionPane.showMessageDialog(jPanel, "结束时间格式为yyyy-MM-dd hh:mm:ss");
                    }
                }
            }
        });

        selDirButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                JFileChooser chooser = new JFileChooser();
                chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
                if (chooser.showOpenDialog(jPanel) == JFileChooser.APPROVE_OPTION) {
                    File gitDir = chooser.getSelectedFile();
                    dirTextField.setText(gitDir.getAbsolutePath());
                }
            }

        });

        findButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        findButton.setEnabled(false);
                        progressBar1.setVisible(true);
                        try {
                            findFileList();
                        } catch (Exception ex) {
                            if(ex instanceof BizException) {
                                JOptionPane.showMessageDialog(jPanel, ((BizException) ex).getMsg());
                            } else {
                                JOptionPane.showMessageDialog(jPanel, "软件出错，请重启软件");
                            }
                        }
                        findButton.setEnabled(true);
                        progressBar1.setVisible(false);
                    }
                }).start();

            }
        });

        fileListTextArea.setEditable(false);

        progressBar1.setOrientation(JProgressBar.HORIZONTAL);
        progressBar1.setIndeterminate(true);
        progressBar1.setVisible(false);
    }

    private void findFileList() throws Exception {
        String dirTextFieldText = dirTextField.getText();
        if(StringUtils.isBlank(dirTextFieldText)) {
            throw new BizException("请选择.git目录");
        }
        String startTimeStr = startTimeTextField.getText();
        String endTimeStr = endTimeTextField.getText();
        Date startTime = null;
        Date endTime = null;
        if (StringUtils.isNotBlank(startTimeStr)) {
            try {
                startTime = DateUtils.parseDate(startTimeStr, "yyyy-MM-dd hh:mm:ss");
            } catch (ParseException p) {
                throw new BizException("起始时间格式为yyyy-MM-dd hh:mm:ss");
            }
        }
        if (StringUtils.isNotBlank(endTimeStr)) {
            try {
                endTime = DateUtils.parseDate(endTimeStr, "yyyy-MM-dd hh:mm:ss");
            } catch (ParseException p) {
                throw new BizException("结束时间格式为yyyy-MM-dd hh:mm:ss");
            }
        }
        StringBuilder buf = new StringBuilder();
        List<DiffEntry> fileList = getFileList(dirTextFieldText, authorTextField.getText(), startTime, endTime);
        if (CollectionUtils.isNotEmpty(fileList)) {
            Set<String> fileSet = new HashSet<>();
            for (DiffEntry entry : fileList) {
                String path = "";
                switch (entry.getChangeType()) {
                    case ADD:
                        path = entry.getNewPath();
                        break;
                    case COPY:
                        path = entry.getNewPath();
                        break;
                    case DELETE:
                        path = entry.getOldPath();
                        break;
                    case MODIFY:
                        path = entry.getOldPath();
                        break;
                    case RENAME:
                        path = entry.getNewPath();
                }
                if(fileSet.add(path)) {
                    buf.append(path + "\r\n");
                }
            }
        }
        fileListTextArea.setText(buf.toString());
    }

    private List<DiffEntry> getFileList(String gitDir, String author, Date startTime, Date endTime) throws Exception {
        Git git = Git.open(new File(gitDir));
        Iterable<RevCommit> results = git.log().all().setRevFilter(new RevFilter() {
            @Override
            public boolean include(RevWalk walker, RevCommit cmit)
                    throws StopWalkException, MissingObjectException, IncorrectObjectTypeException, IOException {
                ;
                boolean isInclude = true;
                if (StringUtils.isNotBlank(author)) {
                    isInclude = isInclude && cmit.getAuthorIdent().getName().equals(author);
                }
                if (startTime != null) {
                    isInclude = isInclude && (cmit.getAuthorIdent().getWhen().compareTo(startTime) >= 0);
                }
                if (endTime != null) {
                    isInclude = isInclude && (cmit.getAuthorIdent().getWhen().compareTo(endTime) <= 0);
                }
                return isInclude;
            }

            @Override
            public RevFilter clone() {
                return this;
            }
        }).call();
        List<DiffEntry> list = new ArrayList<>();
        for (RevCommit commit : results) {
            List<DiffEntry> commitLogList = getCommitLogList(git, commit.getName());
            if (CollectionUtils.isNotEmpty(commitLogList)) {
                list.addAll(commitLogList);
            }
        }
        return list;
    }

    /**
     * @方法简介 :根据指定版本号获取版本号下面的详情记录
     */
    @SuppressWarnings("resource")
    private static List<DiffEntry> getCommitLogList(Git git, String revision) throws Exception {
        // 通过git获取项目库
        Repository repository = git.getRepository();
        // 根据所需要查询的版本号查新ObjectId
        ObjectId objId = repository.resolve(revision);

        //根据版本号获取指定的详细记录
        Iterable<RevCommit> allCommitsLater = git.log().add(objId).call();
        if (allCommitsLater == null) {
            throw new Exception("该提交版本下无当前查询用户的提交记录");
        }
        Iterator<RevCommit> iter = allCommitsLater.iterator();
        RevCommit commit = iter.next();//提交对象

        // 给存储库创建一个树的遍历器
        TreeWalk tw = new TreeWalk(repository);
        // 将当前commit的树放入树的遍历器中
        tw.addTree(commit.getTree());

        commit = iter.next();
        if (commit != null) {
            tw.addTree(commit.getTree());
        } else {
            return null;
        }

        tw.setRecursive(true);
        RenameDetector rd = new RenameDetector(repository);
        rd.addAll(DiffEntry.scan(tw));
        //获取到详细记录结果集    在这里就能获取到一个版本号对应的所有提交记录（详情！！！）
        List<DiffEntry> list = rd.compute();
        return list;
    }

    public void exit() throws IOException {
        File file = new File(CONFIG_PROPERTIES);
        if (!file.exists()) {
            file.createNewFile();
        }
        properties.setProperty(GIT_DIR_KEY, dirTextField.getText());
        properties.setProperty(AUTHOR_KEY, authorTextField.getText());
        properties.setProperty(START_TIME_KEY, startTimeTextField.getText());
        properties.setProperty(END_TIME_KEY, endTimeTextField.getText());
        FileOutputStream out = new FileOutputStream(file);
        properties.store(out, "save config");
        out.close();
    }
}
